package net.gdface.utils;


import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.common.base.Optional;
import com.google.common.base.Predicate;
import com.google.common.base.Strings;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Iterables;

import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Strings.isNullOrEmpty;
import static com.google.common.base.Strings.nullToEmpty;
import static net.gdface.utils.MatchPatternInfo.nullPattern;

/**
 * 支持字符串模糊匹配的规则管理基类(线程安全)<br>
 * @author guyadong
 *
 */
public abstract class BaseStringMatchRules {
	/** 字符串规则基本格式,前缀字段前后用{@code <>}包括,允许前缀字段为空 */
	private static final String RULE_FMT_REGEX="^(?:<(.*?)>)?(.*)$";
	/**
	 * 顺序保存的所有规则对象
	 */
	private final LinkedHashMap<String, StringMatchRule> rules = new LinkedHashMap<>();
	
	/**
	 * {@link #rules}的读写保护锁
	 */
	private final ReadWriteLock ruleLock = new ReentrantReadWriteLock();
	
	private static final StringMatchRule NULL_RULE = new StringMatchRule();
	protected final LoadingCache<String, StringMatchRule> portRuleCache = CacheBuilder.newBuilder().build(new CacheLoader<String, StringMatchRule>(){

		@Override
		public StringMatchRule load(String portName) throws Exception {
			StringMatchRule r = getRule(portName);
			return null == r ? NULL_RULE : r;
		}});
	public BaseStringMatchRules() {
	}

	/**
	 * 添加一条规则
	 * @param rule
	 * @return 当前对象
	 */
	protected BaseStringMatchRules addRule(StringMatchRule rule) {
		Lock lock = ruleLock.writeLock();
		lock.lock();
		try {
			rules.put(rule.keyOfRule(), rule);
			return this;
			
		} finally {
			lock.unlock();
		}
	}
	/**
	 * 添加一条规则
	 * @param rule
	 * @return 当前对象
	 */
	public BaseStringMatchRules addRule(String rule) {
		StringMatchRule r = parseRule(rule);
		return addRule(r);
	}
	/**
	 * 添加一组以,分割的规则
	 * @param rules
	 * @return 当前对象
	 */
	public BaseStringMatchRules addRules(String rules) {
		LinkedHashMap<String, StringMatchRule> rs = parseRules(rules);
		Lock lock = ruleLock.writeLock();
		lock.lock();
		try {
			BaseStringMatchRules.this.rules.putAll(rs);
			return this;			
		} finally {
			lock.unlock();
		}
	}
	/**
	 * 删除指定的规则
	 * @param key
	 * @return 当前对象
	 */
	public BaseStringMatchRules removeKey(String key) {
		Lock lock = ruleLock.writeLock();
		lock.lock();
		try {
			rules.remove(stripQuoteOfPrefix(key));
			return this;
		} finally {
			lock.unlock();
		}
	}
	/**
	 * 替换已经存在的规则
	 * @param value
	 * @return 当前对象
	 */
	public BaseStringMatchRules replace(String value) {
		StringMatchRule r = parseRule(value);
		Lock lock = ruleLock.writeLock();
		lock.lock();
		try {
			TypeUtils.replace(rules,r.keyOfRule(), r);
			return this;			
		} finally {
			lock.unlock();
		}
	}
	/**
	 * 清除所有规则,同时清除缓存规则
	 * @return 当前对象
	 * @see #clearCache()
	 */
	public BaseStringMatchRules clearRules() {
		clearCache();
		Lock lock = ruleLock.writeLock();
		lock.lock();
		try {
			rules.clear();
			clearCache();
			return this;			
		} finally {
			lock.unlock();
		}
	}

	/**
	 * 清除rule缓存对象 {@link #portRuleCache}
	 * @return 当前对象
	 */
	public BaseStringMatchRules clearCache(){
		portRuleCache.asMap().clear();
		return this;
	}
	/**
	 * 顺序返回所有规则的匹配模式字符串
	 */
	public Set<String> patterns(){
		Lock lock = ruleLock.readLock();
		lock.lock();
		try {
			return rules.keySet();
		} finally {
			lock.unlock();
		}
	}
	/**
	 * 顺序返回所有规则
	 */
	public Collection<StringMatchRule> allRules(){
		Lock lock = ruleLock.readLock();
		lock.lock();
		try {
			return rules.values();
		} finally {
			lock.unlock();
		}
	}

	/**
	 * 返回规则数量
	 */
	public int sizeOfRules() {
		Lock lock = ruleLock.readLock();
		lock.lock();
		try {
			return rules.size();
		} finally {
			lock.unlock();
		}
	}

	/**
	 * 从规则列表({@link #rules})中查找服务接口名对应的规则,没有找到返回{@code null}<br>
	 * 查找逻辑:<br>
	 * 先以{@code matchPattern}在规则列表key字段查找是否有匹配的规则,有则返回,
	 * 如果没有找到再在规则列表中顺序以模糊匹配方式查找是否有匹配的规则,有则返回,
	 * 如果没有找到返回{@code null}
	 * @param input 待匹配字符串,为{@code null}则返回{@code null}
	 * @return Rule 对象,没有找到则返回{@code null}
	 */
	public StringMatchRule getRule(final String input) {
		if(!isNullOrEmpty(input)) {
			Lock lock = ruleLock.readLock();
			lock.lock();
			try {
				// 先尝试精确匹配key字段
				StringMatchRule r = rules.get(stripQuoteOfPrefix(input));
				if (null != r) {
					return r;
				}
				final StringMatchRule inputPattern = StringMatchRule.create(input);
				// 顺序模糊匹配查找规则,找到一个就返回
				Optional<StringMatchRule> opt = Iterables.tryFind(rules.values(), 
						new Predicate<StringMatchRule>() {
					@Override
					public boolean apply(StringMatchRule rule) {
						return (!rule.patternInfo.isDefaultMatch() || (null !=rule.prefix && !rule.prefix.isDefaultMatch())) 
								&& rule.match(inputPattern);
					}
				});
				return opt.orNull();
			} finally {
				lock.unlock();
			}
		}
		return null;
	}
	/**
	 * 返回指定的规则在规则表中的索引值,
	 * 输入参数为{@code null}或没有在规则表中找到完全相同的规则则返回-1
	 * @param matchRule
	 */
	public int indexOf(StringMatchRule matchRule){
		int index = -1;
		if(null != matchRule && !matchRule.nullRule()){
			Lock lock = ruleLock.readLock();
			lock.lock();
			try {
				for(StringMatchRule r : rules.values()){
					index ++;
					if(r.equals(matchRule)){
						return index; 
					}
				}
			} finally {
				lock.unlock();
			}
		}
		return index;
	}
	public static String stripQuoteOfPrefix(String pattern){
		if(!isNullOrEmpty(pattern)){
			Pattern p = Pattern.compile(RULE_FMT_REGEX);
			Matcher m = p.matcher(pattern);
			if(m.matches()){
				pattern = nullToEmpty(m.group(1)) + m.group(2);
			}			
		}
		return pattern;
	}
	/**
	 * 返回根据{@code prefix}和{@code portName}字段生成的匹配规则字符串
	 */
	public static String wrapMatchRule(String prefix,String portName){
		return "<"+nullToEmpty(prefix)+">" + nullToEmpty(portName);
	}
	/**
	 * 从规则列表缓存({@link #portRuleCache})查找输入字符串对应的规则,没有找到返回{@link NULL_RULE}<br>
	 * @param input
	 */
	public StringMatchRule getRuleCached(String input) {
		return portRuleCache.getUnchecked(input);
	}
	/**
	 * 先以{@code input}调用{@link #getRuleCached(String)}查找规则，
	 * 如果没找到且{@code addationalPrefix}不为空且{@code input}没有前缀,则将{@code input}加上附加前缀{@link addationalPrefix}再试一次
	 * @param addationalPrefix
	 * @param input
	 * @see #getRuleCached(String)
	 */
	public StringMatchRule getRuleCached(String addationalPrefix, String input) {
		StringMatchRule rule = getRuleCached(input);
		if(rule.nullRule() && !isNullOrEmpty(addationalPrefix)){
			StringMatchRule matchPatternInfo = StringMatchRule.create(input);
			if(null == matchPatternInfo.prefix){
				/** 如果input没有前缀,则加上接口类名前缀再试一次 */
				String matchRule = wrapMatchRule(addationalPrefix,matchPatternInfo.pattern);
				rule = getRuleCached(matchRule);
			} 
		}
		return rule;
	}
	/**
	 * 从规则列表({@link #rules})中根据匹配模式查找匹配模式字符串对应的规则,没有找到返回{@code null}<br>
	 * 如果没有找到返回{@code null}
	 * @param pattern 匹配模式,为{@code null}或空则返回{@code null}
	 * @return StringMatchRule 对象,没有找到则返回{@code null}
	 */
	public StringMatchRule getRuleByPattern(String pattern) {
		if(!Strings.isNullOrEmpty(pattern)) {
			pattern = stripQuoteOfPrefix(pattern);
			Lock lock = ruleLock.readLock();
			lock.lock();
			try {
				return  rules.get(pattern);
			} finally {
				lock.unlock();
			}
		}
		return null;
	}

	@Override
	public String toString() {
		Lock lock = ruleLock.readLock();
		lock.lock();
		try {
			return "BaseStringMatchRules [rules=" + rules + "]";
		} finally {
			lock.unlock();
		}
	}
	/**
	 * 将{@code input}用{@code delim}指定的分隔符切分为不含空格和分隔符的一组字符串
	 * @param input 输入字符串
	 * @param delim 包含多个分隔符的字符串
	 * @return {@code input}或{@code delim}为{@code null}时返回空表
	 */
	private static final List<String> elementsOf(String input,String delim) {
		List<String> list = new ArrayList<String>();
		if (input != null && delim != null) {
			StringTokenizer st = new StringTokenizer(input, delim);
			while (st.hasMoreTokens()) {
				list.add(st.nextToken());
			}
		}
		return list;
	}
	/**
	 * 解析以,分割的多条规则定义
	 * @param rules 以,分割的多条规则定义字符串
	 * @return 解析的规则对象
	 */
	protected LinkedHashMap<String, StringMatchRule> parseRules(String rules) {
		LinkedHashMap<String, StringMatchRule> m = new LinkedHashMap<>();
		if(null != rules) {
			List<String> rulelist = elementsOf(rules," ,\t\n\r\f");
			for(String r: rulelist) {
				StringMatchRule rule = parseRule(r);
				TypeUtils.putIfAbsent(m,rule.pattern, rule);
			}
		}
		return m; 
	}
	/**
	 * 将一条规则字符串解析为 Rule 对象,解析失败则抛出{@link IllegalArgumentException}异常
	 * @param rule 规则定义字符串
	 * @return StringMatchRule 对象
	 */
	protected abstract StringMatchRule parseRule(String rule);
	/**
	 * 字符串匹配规则基类
	 * @author guyadong
	 *
	 */
	public static class StringMatchRule{
		/**
		 * 原始的匹配字符串,允许的格式:<br>
		 * <li>只包含数字字母下划线--精确匹配服务接口名,如 getUser</li>
		 * <li>以*结束--左侧匹配,如 getUser*</li>
		 * <li>以*开始--右侧匹配,如 *User</li>
		 * <li>其他:正则表达式--正则表达式匹配,如 getUser(WithName)?</li>
		 */
		public final String pattern;
		public final MatchPatternInfo patternInfo;
		public final MatchPatternInfo prefix;
		public StringMatchRule(String pattern) {
			this(pattern,true);
		}
		public StringMatchRule(String pattern,boolean withPrefix) {
			if(!Strings.isNullOrEmpty(pattern)){
				String px = null;
				if(withPrefix){
					Pattern p = Pattern.compile(RULE_FMT_REGEX);
					Matcher m = p.matcher(pattern);
					checkArgument(m.matches(),"INVALID pattern %s",pattern);
					px = nullToEmpty(m.group(1));
					pattern = m.group(2);
					this.pattern = px + pattern;
					if("/*/".equals(px)){
						px = "/.*/";
					}
				}else {
					this.pattern = pattern;
				}
				patternInfo = MatchPatternInfo.of(pattern);
				prefix = MatchPatternInfo.of(px);
			}else {
				patternInfo = null;
				prefix = null;
				this.pattern = pattern;
			}
		}

		public StringMatchRule() {
			this(null);
		}

		/**
		 * @return return true if pattern or patternInfo is null
		 */
		public boolean nullRule(){
			return null == pattern || null == patternInfo || patternInfo.nullPattern();
		}
		public String keyOfRule(){
			return pattern;
		}
		
		public boolean match(StringMatchRule matchRule){
			if(!nullRule() && !nullRule(matchRule)){
				boolean matched = patternInfo.match(matchRule.patternInfo);
				if(!nullPattern(prefix)){
					/** prefix为任意通配符时忽略前缀匹配 */
					if(!MatchPatternInfo.ANY.equals(prefix)){
						matched &= prefix.match(matchRule.prefix);
					}
				}else {
					matched &= nullPattern(matchRule.prefix);
				}
				return matched;
			}
			return false;
		}
		public boolean match(String input){
			if(null != input){
				StringMatchRule matchRule = create(input,null != prefix);
				return match(matchRule);
			}
			return false;
		}

		@Override
		public String toString() {
			return "StringMatchRule [" + (pattern != null ? "pattern=" + pattern + ", " : "")
					+ (patternInfo != null ? "patternInfo=" + patternInfo + ", " : "")
					+ (prefix != null ? "prefix=" + prefix : "") + "]";
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((pattern == null) ? 0 : pattern.hashCode());
			result = prime * result + ((patternInfo == null) ? 0 : patternInfo.hashCode());
			result = prime * result + ((prefix == null) ? 0 : prefix.hashCode());
			return result;
		}

		@Override
		public boolean equals(Object obj) {
			if (this == obj)
				return true;
			if (obj == null)
				return false;
			if (getClass() != obj.getClass())
				return false;
			StringMatchRule other = (StringMatchRule) obj;
			if (pattern == null) {
				if (other.pattern != null)
					return false;
			} else if (!pattern.equals(other.pattern))
				return false;
			if (patternInfo == null) {
				if (other.patternInfo != null)
					return false;
			} else if (!patternInfo.equals(other.patternInfo))
				return false;
			if (prefix == null) {
				if (other.prefix != null)
					return false;
			} else if (!prefix.equals(other.prefix))
				return false;
			return true;
		}
		/**
		 * 根据模式字符串({@code pattern})创建{@link StringMatchRule}对象
		 * @param pattern
		 * @param withPrefix
		 * @return 返回归一化的模式字符串和对应的匹配类型
		 */
		public static StringMatchRule create(String pattern,boolean withPrefix) {
			return new StringMatchRule(pattern,withPrefix);
		}
		/**
		 * 根据模式字符串({@code pattern})创建{@link StringMatchRule}对象
		 * @param pattern
		 * @return 返回归一化的模式字符串和对应的匹配类型
		 */
		public static StringMatchRule create(String pattern) {
			return create(pattern,true);
		}
		/**
		 * @return @return return true if rule or rule.pattern or rule.patternInfo is null
		 */
		public static boolean nullRule(StringMatchRule rule){
			return null == rule || rule.nullRule();
		}
	}
}
